package wordit.ea.selectionfunctions;

import java.util.ArrayList;
import java.util.List;

import wordit.ea.Genome;
import wordit.ea.SelectionFunction;

public class TournamentSelectionFunction implements SelectionFunction {

	private int roundsCount;

	public TournamentSelectionFunction(int i) {
		setTournamentSize(i);
	}

	@Override
	public Genome[] selectParents(Genome[] generation) {
		Genome[] parents = new Genome[2];

		parents[0] = selectParent(generation);
		parents[1] = selectParent(generation);

		return parents;
	}

	private Genome selectParent(Genome[] generation) {
		final int firstRoundSize = (int) Math.floor(Math.pow(2,
				getRoundsCount()));
		List<Genome> currentRound = selectParticipants(generation,firstRoundSize);
		
		while (currentRound.size() > 1) {
			currentRound = playRound(currentRound);
		}

		return currentRound.get(0);
	}

	/**
	 * Randomly selects participants from the generation
	 * 
	 * @param generation generation to select from
	 * @param firstRoundSize number of participants to select
	 * @return 
	 */
	private List<Genome> selectParticipants(Genome[] generation, int firstRoundSize) {
		List<Genome> firstRound = new ArrayList<Genome>(firstRoundSize);
		
		for (int i=0;i<firstRoundSize; ++i) {
			final int participantIndex = (int) Math.floor(Math.random() * generation.length);
			firstRound.add(generation[participantIndex]);
		}
		
		return firstRound;
	}

	private List<Genome> playRound(List<Genome> currentRound) {
		List<Genome> nextRound = new ArrayList<Genome>(currentRound.size()/2);
		
		for (int i = 0; i < currentRound.size(); i += 2) {
			final Genome p1 = currentRound.get(i);
			final Genome p2 = currentRound.get(i + 1);
			final Genome winner = (p1.getFitness() > p2.getFitness()) ? p1
					: p2;
			nextRound.add(winner);
		}
		
		return nextRound;
	}

	private int getRoundsCount() {
		return roundsCount;
	}

	public void setTournamentSize(int n) {
		if (n < 1)
			throw new IllegalArgumentException(
					"tournament must have at least 1 round");

		roundsCount = n;
	}

	public int getTournamentSize() {
		return roundsCount;
	}

}
